Skip to content

使用 Blocks 自定义聊天机器人

介绍

重要提示:如果您刚开始接触,我们建议使用 gr.ChatInterface 来创建聊天机器人——它是一个高级抽象,可以快速创建漂亮的聊天机器人应用程序,通常只需一行代码。可以查看 创建聊天机器人 (ChatInterface) 了解更多信息。

本教程将展示如何使用 Gradio 的低级 Blocks API 从头开始构建聊天机器人 UI。这将使您完全控制您的聊天机器人 UI。您将首先创建一个简单的聊天机器人来显示文本,然后是一个支持流式文本响应的聊天机器人,最后创建一个也可以处理媒体文件的聊天机器人。

前提条件:我们将使用 gradio.Blocks 类来构建聊天机器人演示。如果您还不熟悉它,可以先阅读 Blocks 与事件监听 指南。另外,请确保您使用的是 Gradio 的最新版本:

bash
pip install --upgrade gradio

一个简单的聊天机器人演示

让我们从创建一个简单的演示开始。我们的机器人将对任何输入随机回答一个预设消息。以下是使用 Gradio 创建这个聊天机器人的代码:

python
import gradio as gr
import random
import time

with gr.Blocks() as demo:
    chatbot = gr.Chatbot(type="messages")
    msg = gr.Textbox()
    clear = gr.ClearButton([msg, chatbot])

    def respond(message, chat_history):
        bot_message = random.choice(["你好吗?", "今天是个好日子", "我很饿"])
        chat_history.append({"role": "user", "content": message})
        chat_history.append({"role": "assistant", "content": bot_message})
        time.sleep(2)  # 模拟思考时间
        return "", chat_history

    msg.submit(respond, [msg, chatbot], [msg, chatbot])

demo.launch()

在这个示例中,我们使用了三个 Gradio 组件:

  • 一个 Chatbot 组件,其值存储了用户和机器人之间对话的完整历史,作为一个消息列表。
  • 一个 Textbox 组件,用户在其中输入消息,然后按回车键提交以触发聊天机器人的回应。
  • 一个 ClearButton 按钮来清除文本框和整个聊天机器人的历史记录。

我们定义了一个 respond() 函数,它接收用户消息和聊天历史,在历史中添加一个随机机器人消息,等待 2 秒钟,然后返回更新后的聊天历史。respond() 函数在返回时也会清除文本框。

在实际应用中,您会用自己更复杂的函数替换 respond(),可能会调用预训练模型或 API 来生成回应。

添加流式输出到您的聊天机器人

我们可以通过几种方式改善上述聊天机器人的用户体验:

  1. 使用流式传输响应,这样用户就不必等待整个消息生成完毕。
  2. 让用户消息立即显示在聊天历史中,同时生成聊天机器人的回应。

以下是实现这些功能的代码:

python
import gradio as gr
import random
import time

with gr.Blocks() as demo:
    chatbot = gr.Chatbot(type="messages")
    msg = gr.Textbox()
    clear = gr.Button("清除")

    def user(user_message, history):
        # 立即显示用户消息,但机器人尚未回应
        return "", history + [{"role": "user", "content": user_message}]

    def bot(history):
        # 生成并流式传输机器人的回应
        bot_message = random.choice(["你好吗?", "我爱你", "我很饿"])
        history.append({"role": "assistant", "content": ""})
        # 逐个字符构建消息
        for character in bot_message:
            history[-1]["content"] += character
            time.sleep(0.05)  # 每个字符之间添加延迟
            yield history

    # 链式事件:先处理用户输入,再生成机器人回应
    msg.submit(user, [msg, chatbot], [msg, chatbot], queue=False).then(
        bot, chatbot, chatbot
    )
    clear.click(lambda: None, None, chatbot, queue=False)

# 启用队列功能,这对于流式传输是必需的
demo.queue()
demo.launch()

您会注意到,当用户提交消息时,我们现在将两个事件与 .then() 链接起来:

  1. 第一个方法 user() 会立即用用户的消息更新聊天机器人,并清空输入字段。因为我们希望这能立即发生,我们设置了 queue=False,如果启用了队列,它将跳过排队。
  2. 第二个方法 bot() 会用机器人的回应更新聊天机器人的历史记录。我们逐个字符地构建消息,并使用 yield 产生中间输出。Gradio 会自动将任何带有 yield 关键字的函数转变为流输出接口。

在实际应用中,您会用自己更复杂的函数替换 bot(),可能会调用预训练模型或 API 来生成流式回应。

最后,我们通过运行 demo.queue() 来启用排队,这对于流式传输中间输出是必需的。

添加标记反馈功能

一旦创建了 gr.Chatbot,您可以添加让用户对消息表示喜欢或不喜欢的功能。如果您希望用户对机器人的回复进行评价或标记不当的内容,这个功能会很有用。

要将此功能添加到聊天机器人中,只需将一个 .like() 事件附加到您的聊天机器人即可。拥有 .like() 事件的聊天机器人将会在每条机器人消息旁边自动显示一个点赞图标和一个点踩图标。

.like() 方法要求您传入一个函数,当用户点击这些图标时会调用该函数。在您的函数中,应该有一个参数,其类型为 gr.LikeData。以下是一个简单的示例:

python
import gradio as gr

def greet(history, input):
    return history + [{"role": "user", "content": input}, {"role": "assistant", "content": "你好," + input}]

def vote(data: gr.LikeData):
    if data.liked:
        print("您点赞了这个回复: " + data.value["content"])
    else:
        print("您点踩了这个回复: " + data.value["content"])

with gr.Blocks() as demo:
    chatbot = gr.Chatbot(type="messages")
    textbox = gr.Textbox()
    textbox.submit(greet, [chatbot, textbox], [chatbot])
    
    # 添加这行代码会在聊天机器人中显示点赞/点踩图标
    chatbot.like(vote, None, None)
    
demo.launch()

添加 Markdown、图片、音频或视频

gr.Chatbot 组件支持部分 Markdown 语法,包括粗体、斜体和代码。例如,我们可以编写一个这样的函数,它对用户的消息作出响应,并用粗体显示"太酷了!":

python
def bot(history):
    response = "**太酷了!**"
    history.append({"role": "assistant", "content": response})
    return history

此外,它可以处理媒体文件,如图像、音频和视频。您可以使用 MultimodalTextbox 组件轻松地将各种类型的媒体文件上传到您的聊天机器人。要传入媒体文件,我们必须将文件路径作为字典传入,包含 path 键:

python
def add_message(history, message):
    for x in message["files"]:
        history.append({"role": "user", "content": {"path": x}})
    if message["text"] is not None:
        history.append({"role": "user", "content": message["text"]})
    return history, gr.MultimodalTextbox(value=None, interactive=False, file_types=["image"])

将这些结合起来,我们可以创建一个多模态聊天机器人,它有一个多模态文本框,用户可以提交文本和媒体文件:

python
import gradio as gr
import time

# 多模态聊天机器人示例,支持文本和媒体文件,并展示流式文本输出

def print_like_dislike(x: gr.LikeData):
    print(x.index, x.value, x.liked)  # 打印点赞/点踩数据

def add_message(history, message):
    for x in message["files"]:
        history.append({"role": "user", "content": {"path": x}})
    if message["text"] is not None:
        history.append({"role": "user", "content": message["text"]})
    return history, gr.MultimodalTextbox(value=None, interactive=False)

def bot(history):
    response = "**太酷了!**"
    history.append({"role": "assistant", "content": ""})
    for character in response:
        history[-1]["content"] += character
        time.sleep(0.05)
        yield history

with gr.Blocks() as demo:
    chatbot = gr.Chatbot(
        [],
        elem_id="chatbot",
        bubble_full_width=False,
        type="messages"
    )
    
    chat_input = gr.MultimodalTextbox(
        interactive=True,
        file_count="multiple",
        placeholder="输入消息或上传文件...",
        show_label=False,
        sources=["microphone", "upload"],
    )
    
    chat_msg = chat_input.submit(
        add_message, [chatbot, chat_input], [chatbot, chat_input]
    )
    bot_msg = chat_msg.then(bot, chatbot, chatbot, api_name="bot_response")
    bot_msg.then(lambda: gr.MultimodalTextbox(interactive=True), None, [chat_input])
    
    chatbot.like(print_like_dislike, None, None)

demo.queue()
demo.launch()

完成了!这就是构建自定义聊天机器人 UI 所需的全部代码。您可以基于此框架,集成您自己的大型语言模型或其他 AI 系统,创建功能丰富的聊天应用程序。

进阶示例

下面是一些可以参考的高级聊天机器人示例,这些项目在 Hugging Face Spaces 上运行:

  1. Baize-7B:一个风格化的聊天机器人,允许用户停止生成以及重新生成响应。
  2. mPLUG-Owl:一个多模态聊天机器人,允许用户对回答进行点赞和点踩。

这些示例展示了如何将 Gradio Blocks 与强大的 AI 模型结合,创建专业级的聊天应用程序。